package com.ideaplugin.datamanager.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.ideaplugin.datamanager.common.CommonValidEnum;
import com.ideaplugin.datamanager.common.PageUtil;
import com.ideaplugin.datamanager.common.UserInfoHolder;
import com.ideaplugin.datamanager.entity.*;
import com.ideaplugin.datamanager.manager.*;
import com.ideaplugin.datamanager.model.UserTableAuth;
import com.ideaplugin.datamanager.model.common.PageRequest;
import com.ideaplugin.datamanager.model.common.PageResult;
import com.ideaplugin.datamanager.model.common.UserInfo;
import com.ideaplugin.datamanager.model.request.CustomizedColumnInfoParam;
import com.ideaplugin.datamanager.model.request.TableColumnParam;
import com.ideaplugin.datamanager.model.response.TableColumnVo;
import com.ideaplugin.datamanager.model.response.TableInfoVo;
import com.ideaplugin.datamanager.model.response.UserAuthColumnVo;
import com.ideaplugin.datamanager.service.TableColumnService;
import com.ideaplugin.datamanager.service.TableInfoService;
import com.ideaplugin.datamanager.service.UserTableService;
import com.mybatisflex.core.paginate.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.ideaplugin.datamanager.entity.table.TableAuthTableDef.TABLE_AUTH;
import static com.ideaplugin.datamanager.entity.table.TableColumnAuthTableDef.TABLE_COLUMN_AUTH;
import static com.ideaplugin.datamanager.entity.table.TableColumnTableDef.TABLE_COLUMN;
import static com.ideaplugin.datamanager.entity.table.TableColumnUserCustomizedTableDef.TABLE_COLUMN_USER_CUSTOMIZED;
import static com.ideaplugin.datamanager.entity.table.TableInfoTableDef.TABLE_INFO;

/**
 * Created on 2022/6/15.
 *
 * @author zhaojb
 */
@Service
public class UserTableServiceImpl implements UserTableService {
    @Autowired
    private TableInfoService tableInfoService;
    @Resource
    private TableColumnAuthManager tableColumnAuthManager;
    @Resource
    private TableColumnService tableColumnService;
    @Resource
    private TableColumnManager tableColumnManager;
    @Resource
    private TableAuthManager tableAuthManager;
    @Resource
    private TableColumnUserCustomizedManager userCustomizedManager;
    @Resource
    private SysUserRoleManager sysUserRoleManager;
    @Resource
    private TableInfoManager tableInfoManager;

    @Override
    public PageResult<TableInfoVo> pageUserAuthTable(PageRequest pageRequest) {
        UserInfo userInfo = UserInfoHolder.getUserInfo();
        if (userInfo.isAdmin()) {
            final Page<TableInfoVo> page = tableInfoManager.queryChain()
                    .from(TableInfo.class)
                    //.where(TableInfoTableDef.TABLE_INFO.TABLE_NAME.like(query.getName()))
                    //.where(TableInfoTableDef.TABLE_INFO.DATA_SOURCE.eq(query.getDataSource()))
                    .orderBy(TABLE_INFO.CREATE_TIME, false)
                    .pageAs(PageUtil.convert(pageRequest), TableInfoVo.class);
            return PageUtil.convert(page);
        }

        final Page<TableInfoVo> page = tableAuthManager.queryChain().select(TABLE_INFO.ALL_COLUMNS)
                .from(TABLE_AUTH)
                .leftJoin(TABLE_INFO).on(TABLE_AUTH.TABLE_INFO_ID.eq(TABLE_INFO.ID))
                .where(TABLE_AUTH.ROLE_ID.in(userInfo.getRoleIds()))
                .where(TABLE_AUTH.AUTH_VALID.eq(CommonValidEnum.valid.getValue()))
                .pageAs(PageUtil.convert(pageRequest), TableInfoVo.class);
        return PageUtil.convert(page);

    }

    @Override
    public UserAuthColumnVo getUserAuthColumn(Long tableInfoId) {
        UserAuthColumnVo result = new UserAuthColumnVo();
        UserInfo userInfo = UserInfoHolder.getUserInfo();
        final UserTableAuth userTableAuth = listUserAuthColumnCodes(userInfo, tableInfoId);
        final List<TableColumnVo> userAuthColumnVos = userTableAuth.getAuthColumns();

        final List<String> userAuthColumnCodes = userAuthColumnVos.stream().map(TableColumnParam::getColumnCode).collect(Collectors.toList());
        //用户没有字段权限
        if (CollUtil.isEmpty(userAuthColumnCodes)) {
            return result;
        }

        final TableColumnUserCustomized userCustomizedColumnInfo = getUserCustomizedColumnInfo(userInfo, tableInfoId);
        result.setDownloadValid(userTableAuth.getDownloadAuth().getValue());
        //用户未自定义字段顺序，return有权限字段的默认顺序
        if (StringUtils.isBlank(userCustomizedColumnInfo.getColumns())) {
            userAuthColumnVos.forEach(columnVo -> columnVo.setShow(CommonValidEnum.valid.getValue()));
            result.setAuthColumns(userAuthColumnVos);
            return result;
        }
        final LinkedHashMap<String, CustomizedColumnInfoParam.ColumnInfo> userCustomizedColumnCodesMap = JSON.parseArray(userCustomizedColumnInfo.getColumns(), CustomizedColumnInfoParam.ColumnInfo.class)
                .stream()
                .collect(Collectors.toMap(CustomizedColumnInfoParam.ColumnInfo::getColumnCode, Function.identity(), (o, n) -> n, LinkedHashMap::new));

        List<String> userCustomizedColumnCodes = Lists.newArrayList(userCustomizedColumnCodesMap.keySet());
        //如果减少了用户的权限字段,则过滤用户有权限的字段
        if (!CollUtil.containsAll(userAuthColumnCodes, userCustomizedColumnCodes)) {
            userCustomizedColumnCodes = userCustomizedColumnCodes.stream()
                    .filter(columnCode -> userAuthColumnCodes.contains(columnCode))
                    .collect(Collectors.toList());
        }
        // 如果增加了用户的权限字段
        userAuthColumnCodes.removeAll(userCustomizedColumnCodes);
        //新增的字段，加到已有的权限顺序后面
        userCustomizedColumnCodes.addAll(userAuthColumnCodes);
        final List<TableColumnVo> customizedColumnCodes = sort(userAuthColumnVos, userCustomizedColumnCodes, userCustomizedColumnCodesMap);
        result.setAuthColumns(customizedColumnCodes);
        return result;
    }

    private List<TableColumnVo> sort(List<TableColumnVo> tableColumnVos, List<String> userCustomizedColumnCodes, LinkedHashMap<String, CustomizedColumnInfoParam.ColumnInfo> userCustomizedColumnCodesMap) {

        final Map<String, TableColumnVo> tableColumnVoMap = tableColumnVos.stream()
                .collect(Collectors.toMap(TableColumnParam::getColumnCode, Function.identity()));
        final List<TableColumnVo> customizedColumnCodes = userCustomizedColumnCodes.stream()
                .map(columnCode -> {
                    final TableColumnVo tableColumnVo = tableColumnVoMap.get(columnCode);
                    final CustomizedColumnInfoParam.ColumnInfo columnInfo = userCustomizedColumnCodesMap.get(columnCode);
                    if (Objects.isNull(columnInfo)) {
                        tableColumnVo.setShow(CommonValidEnum.valid.getValue());
                    } else {
                        tableColumnVo.setShow(columnInfo.getShow());
                    }
                    return tableColumnVo;
                }).collect(Collectors.toList());
        return customizedColumnCodes;
    }

    private List<TableColumnVo> listSortColumnCodes(Long tableInfoId, Collection<String> userAuthColumnCodes) {
        final List<TableColumn> tableColumnVos = tableColumnManager.queryChain().from(TableColumn.class)
                .orderBy(TABLE_COLUMN.ORDER_NO, true)
                .where(TABLE_COLUMN.TABLE_INFO_ID.eq(tableInfoId))
                .where(TABLE_COLUMN.COLUMN_CODE.in(userAuthColumnCodes))
                .list();

        return tableColumnVos.stream().map(column -> {
            TableColumnVo vo = new TableColumnVo();
            BeanUtils.copyProperties(column, vo);
            if (StringUtils.isNotBlank(column.getDefaultValue())) {
                vo.setDefaultValue(JSON.parse(column.getDefaultValue()));
            }
            return vo;
        }).collect(Collectors.toList());


    }

    @Override
    public Boolean saveUserCustomizedColumnInfo(CustomizedColumnInfoParam customizedColumnInfoParam) {
        UserInfo userInfo = UserInfoHolder.getUserInfo();
        final TableColumnUserCustomized one = getUserCustomizedColumnInfo(userInfo, customizedColumnInfoParam.getTableInfoId());
        one.setTableInfoId(customizedColumnInfoParam.getTableInfoId());
        one.setUserId(userInfo.getUserId());
        one.setColumns(JSON.toJSONString(customizedColumnInfoParam.getColumnInfo()));
        return userCustomizedManager.saveOrUpdate(one);
    }

    private TableColumnUserCustomized getUserCustomizedColumnInfo(UserInfo userInfo, Long tableInfoId) {
        return userCustomizedManager.queryChain().from(TableColumnUserCustomized.class)
                .where(TABLE_COLUMN_USER_CUSTOMIZED.USER_ID.eq(userInfo.getUserId()))
                .where(TABLE_COLUMN_USER_CUSTOMIZED.TABLE_INFO_ID.eq(tableInfoId))
                .oneOpt().orElse(new TableColumnUserCustomized());
    }

    private List<String> listUserCustomizedColumnCodes(UserInfo userInfo, Long tableInfoId) {
        final TableColumnUserCustomized userCustomizedColumnInfo = getUserCustomizedColumnInfo(userInfo, tableInfoId);
        if (StringUtils.isBlank(userCustomizedColumnInfo.getColumns())) {
            return Lists.newArrayList();
        }

        return JSON.parseArray(userCustomizedColumnInfo.getColumns(), CustomizedColumnInfoParam.ColumnInfo.class)
                .stream()
                .filter(columnInfo -> columnInfo.getShow().equals(CommonValidEnum.valid.getValue()))
                .map(CustomizedColumnInfoParam.ColumnInfo::getColumnCode)
                .collect(Collectors.toList());

    }

    private UserTableAuth listUserAuthColumnCodes(UserInfo userInfo, Long tableInfoId) {
        final UserTableAuth result = new UserTableAuth();
        if (userInfo.isAdmin()) {
            final List<TableColumnVo> list = tableColumnService.list(tableInfoId);
            result.setAuthColumns(list);
            result.setDownloadAuth(CommonValidEnum.valid);
            return result;
        }
        // 一个用户多个角色，任意一个角色有权限，即给权限
        final List<TableAuth> tableAuthList = tableAuthManager.queryChain()
                .from(TableAuth.class)
                .where(TABLE_AUTH.TABLE_INFO_ID.eq(tableInfoId))
                .where(UserInfoHolder.adminQueryCondition(TABLE_AUTH.ROLE_ID))
                .where(UserInfoHolder.adminQueryCondition(TABLE_AUTH.AUTH_VALID.eq(CommonValidEnum.valid.getValue())))
                .list();
        if (CollUtil.isEmpty(tableAuthList)) {
            return result;
        }
        tableAuthList.stream()
                .filter(tableAuth -> CommonValidEnum.getByValue(tableAuth.getDownloadValid()) == CommonValidEnum.valid)
                .findFirst()
                .ifPresentOrElse(tableAuth -> result.setDownloadAuth(CommonValidEnum.valid), () -> result.setDownloadAuth(CommonValidEnum.invalid));
        final List<String> columnCodesJson = tableColumnAuthManager.queryChain()
                .from(TableColumnAuth.class)
                .select(TABLE_COLUMN_AUTH.COLUMN_CODE)
                .where(TABLE_COLUMN_AUTH.TABLE_INFO_ID.eq(tableInfoId))
                .where(UserInfoHolder.adminQueryCondition(TABLE_COLUMN_AUTH.ROLE_ID))
                .listAs(String.class);
        final List<String> columnCodes = columnCodesJson.stream()
                .flatMap(codes -> JSON.parseArray(codes).stream().map(String::valueOf))
                .distinct()
                .collect(Collectors.toList());
        final List<TableColumnVo> tableColumnVos = this.listSortColumnCodes(tableInfoId, columnCodes);
        result.setAuthColumns(tableColumnVos);
        return result;
    }

}
